<?php
/**
 * @author Anthony Free
 * @since 20120716
 * @package eclipsemde
 * @subpackage core
 */

/**
 * 
 */

require_once('ProjectEditType.php');

/**
 * Project Class
 *
 * @author Anthony Free
 * @since 20120716
 * @package eclipsemde
 * @subpackage core
 */
class Project implements Stashable {
    // Variable in use by logic etc
    protected $project_identifier = false;
    protected $db_hit_flag = false;
    protected $isValid = false;
    
    // Single Value Project Information
    protected $project_id = false; // Set to -1 on error
    protected $project_name = false;
    protected $project_alias = false;
    protected $project_description = false;
    protected $project_icon_url = false;
    protected $project_time_creation = false;
    protected $project_time_lastedit = false;
    protected $project_lead_maintainer = false; // UserID Only - Resolve with the UserStash
    protected $project_visible = false;
    
    // Multiple Value Project Information - Some of these maybe array of arrays! Check!
    protected $project_websites = array();
    protected $project_doclinks = array();
    protected $project_blogs = array();
    protected $project_eclipse_u_url = array();
    protected $project_maintainers = array(); // Array of Userid, Resolve with UserStash
    protected $project_rssfeeds = array(); // Used to show a aggregated RSS Feed Box
    protected $project_depd_req = array(); // Dependencies: Required
    protected $project_depd_rec = array(); // Dependencies: Recommended
    protected $project_tags = array(); // Array-of-Arrays, Mapping ->  Array ( TAG_ID, TAG_NAME );
    
    // Social Information
    protected $project_displayTwitter = false; // This is set to a username if a feed is to be shown, max of 1
    protected $project_twitters = array();
    protected $project_avg_rating = false;
    protected $project_num_rating = false;
    //protected $project_facebook = array();
    
    
    /**
     * Constructor for the project class system
     * @param type $project_identifier
     */
    public function __construct($project_identifier = false) {
	global $UserStash;
	$this->project_identifier = $project_identifier;
	try {
	    $row = &$this->resolveIdentifier();
	    // Existance Checking
	    if($row === false) {
		$this->isValid = false;
		return false;
	    } else {
		
		// -------- Setup the initial values that are to be used! --------- //
		// ---- Remember that there is not a need to instanciate unless you need all values ---- //
		$this->isValid = true;
		// Row is an assoc array =)
		$this->project_id = ((int)$row['pid']);
		$this->project_name = $row['name'];
		$this->project_alias = $row['alias'];
		$this->project_description = $row['description'];
		$this->project_icon_url = $row['icon_url'];
		$this->project_visible = $row['visible'];
		try {
		    $UserStash->stash((int)$row['lead_maintainer']);
		} catch(InvalidUserException $e) {
		    // Why this would ever happen with ON UPDATE RESTRICT is beyond me! but hey!
		    trigger_error("Lead Maintainer User stored in database for Project `".$this->project_name."` does not exist.", E_USER_WARNING);
		}
		$this->project_lead_maintainer = ((int)$row['lead_maintainer']);
		$this->project_time_creation = $row['creation_time'];
		$this->project_time_lastedit = $row['lastedit_time'];
		return true;
	    }
	} catch(InvalidArgumentException $e) {
	    // Someone passed null
	    $this->project_id = -1;
	    $this->isValid = false;
	    return false;
	}
    }
    
    /**
     * Return true/false is this is a valid project
     * @return boolean 
     */
    public function isValid() {
	return $this->isValid;
    }
    
    /**
     * Destructor for this class 
     */
    public function __destruct() {}
    
    /**
     * We are supplying 2 methods of creating a class of this type.
     * We can do it by ID number
     * or we can do it by the alias (eg www....../alias)
     * This class will work out who is what
     * @global integer/string $sql_conx
     * @return boolean
     * @throws InvalidArgumentException 
     */
    protected function &resolveIdentifier() {
	global $sql_conx;
	$res_by_num = null;
	$res_by_name = null;
	
	// Do we trust this data?
	$sqlSafe_pident = $sql_conx->real_escape_string($this->project_identifier);
	
	// Check for null, or try to resolve the pid if it is an integer
	if(is_null($this->project_identifier)) {	    
	    throw new InvalidArgumentException("Cannot resolve null.");
	} elseif(is_int($this->project_identifier)) {
	    $query = $sql_conx->query("SELECT * FROM `tool_project` WHERE `pid`='{$sqlSafe_pident}' LIMIT 1;");
	    if($query !== false && mysqli_num_rows($query) === 1) {
		$row = $query->fetch_assoc();
		return $row;
	    }
	    trigger_error('Could not resolve via id number `'.$this->project_identifier.'`');
	    unset($query);
	    $res_by_num = false;
	}
	// Try to resolve the alias
	$query = $sql_conx->query("SELECT * FROM `tool_project` WHERE `alias`='{$sqlSafe_pident}' LIMIT 1;");
	if($query !== false && mysqli_num_rows($query) === 1) {
	    $row = $query->fetch_assoc();
	    return $row;
	} else {
	    $res_by_name = false;
	    trigger_error('Could not resolve via alias `'.$this->project_identifier.'`');
	}
	unset($query);
	$false = false; // returning by reference, slave variable
	return $false;
    }

    /**
     * Collect all information about the project (explicit ask)
     */
    public function instanciate() {
	// Null Guard from __construct();
	if($this->project_id === -1) {
	    return false;
	}
	
	if($this->db_hit_flag === false) {
	    // Do not hit the database twice
	    global $sql_conx;
	    
	    // We are using fetch row here for speed as its a mass of queries
		// ------ Websites ------ //
	    $query = $sql_conx->query("SELECT `website` FROM `tool_websites` WHERE `pid`='{$this->project_id}';");
	    while($row = $query->fetch_row()) {
		$this->project_websites[] = $row[0];
	    }
	    $query = null; unset($query);
		// ------ Doclinks ------ //
	    $query = $sql_conx->query("SELECT `doclink` FROM `tool_doclinks` WHERE `pid`='{$this->project_id}';");
	    while($row = $query->fetch_row()) {
		$this->project_doclinks[] = $row[0];
	    }
	    $query = null; unset($query);
		// ------ Blogs ------ //
	    $query = $sql_conx->query("SELECT `blogurl` FROM `tool_blogs` WHERE `pid`='{$this->project_id}';");
	    while($row = $query->fetch_row()) {
		$this->project_blogs[] = $row[0];
	    }
	    $query = null; unset($query);
		// ------ Eclipse Update URLs ------ //
	    $query = $sql_conx->query("SELECT `e_u_url` FROM `tool_eclipseupdurl` WHERE `pid`='{$this->project_id}';");
	    while($row = $query->fetch_row()) {
		$this->project_eclipse_u_url[] = $row[0];
	    }
	    $query = null; unset($query);
		// ------ Maintainers (Exluding Leader) ------ //
	    $query = $sql_conx->query("SELECT `uid` FROM `tool_maintainers` WHERE `pid`='{$this->project_id}';");
	    while($row = $query->fetch_row()) {
		$this->project_maintainers[] = ((int)$row[0]);
	    }
	    $query = null; unset($query);
		// ------ RSS Feed URIs ------ //
	    $query = $sql_conx->query("SELECT `rssuri` FROM `tool_rssfeeds` WHERE `pid`='{$this->project_id}';");
	    while($row = $query->fetch_row()) {
		$this->project_rssfeeds[] = ($row[0]);
	    }
	    $query = null; unset($query);
		// ------ Required Depencies ------ //
	    $query = $sql_conx->query("SELECT `dependency`, `comment`, `emde_depd_pid`, `is_optional` FROM `tool_depd` WHERE `pid`='{$this->project_id}';");
	    while($row = $query->fetch_assoc()) {
		// USING ASSOC! NOT ROW FOR EASE JUST HERE, TOO MANY COLUMNS!
		$dependency_data = array(
		    'dependency' => $row['dependency'],
		    'comment' => $row['comment'],
		    'emde_depd_pid' => $row['emde_depd_pid']
		);
		if($row['is_optional'] == '1') {
		    $this->project_depd_rec[] = $dependency_data;
		} else {
		    $this->project_depd_req[] = $dependency_data;
		}
	    }
	    $query = null; unset($query);
		/*// ------ Recommended Depencies ------ //
	    $query = $sql_conx->query("SELECT `dependency`, `comment` FROM `tool_depd_rec` WHERE `pid`='{$this->project_id}';");
	    while($row = $query->fetch_row()) {
		$this->project_depd_rec[] = array(0 => $row[0], 1 => $row[1]);
	    }
	    $query = null; unset($query);*/
		// ------ Tags ------ //
	    $query = $sql_conx->query("SELECT `tid` FROM `tool_tags` WHERE `pid`='{$this->project_id}';");
	    while($row = $query->fetch_row()) {
		$this->project_tags[] = ((int)$row[0]);
	    }
	    $query = null; unset($query);
		// ------ Twitter ------ //
	    $query = $sql_conx->query("SELECT `username`, `display` FROM `tool_twitter` WHERE `pid`='{$this->project_id}';");
	    while($row = $query->fetch_row()) {
		if($row[1] == '1') {
		    // We are displaying this one :)
		    $this->project_displayTwitter = $row[0];
		} else {
		    // we are not displaying this, but it will be listed as an account for this project
		    $this->project_twitters[] = $row[0];
		}
	    }
	    $query = null; unset($query);
	    
	    $query = $sql_conx->query("SELECT AVG(`rating`), COUNT(`rating`) FROM `tool_rating` WHERE `pid`='{$this->project_id}';");
	    $row = $query->fetch_row();
	    $this->project_avg_rating = $row[0];
	    $this->project_num_rating = $row[1];
	    
	    $row = null; unset($row);
	    $query = null; unset($query);
	    
	    $this->db_hit_flag = true;
	    return true;
	} else {	
	    //trigger_error('Double calling of '.__CLASS__.'->'.__METHOD__.' somewhere.');
	    return true;
	}
    }
    
    /**
     * Return the ID of this project (pid from database)
     * @return int 
     */
    public function getID() {
	return ((int)$this->project_id);
    }
    
    /**
     * Return the alias (tool short name) for this project
     * @return string 
     */
    public function getAlias() {
	return $this->project_alias;
    }
    
    /**
     * Return the Full Name of this project
     * @return string 
     */
    public function getName() {
	return $this->project_name;
    }
    
    /**
     * Return the timestamp of creation for this project
     * @return type 
     */
    public function getCreationTime() {
	return $this->project_time_creation;
    }
    
    /**
     * Return the timestamp of the last time project was edited/updated
     * @return int 
     */
    public function getLastEditTime() {
	return $this->project_time_lastedit;
    }
    
    /**
     * Return the description of the project
     * @return String 
     */
    public function getDescription() {
	return $this->project_description;
    }
    
    /**
     * Return the ID of the lead maintainer
     * @return int 
     */
    public function getLeadMaintainer() {
	return ((int)$this->project_lead_maintainer);
    }
    
    /**
     * Return the IDs in an array of all maintainers
     * @return array 
     */
    public function getAllMaintainers() {
	$this->instanciate(); // Instanciate if these values do not exist
	$allmembers = $this->project_maintainers;
	$allmembers[] = $this->project_lead_maintainer;
	return $allmembers;
    }
    
    /**
     * Return the IDs of all maintainers, except the lead, in an array
     * @return array 
     */
    public function getNoneLeaderMaintainers() {
	$this->instanciate(); // Instanciate if these values do not exist
	return $this->project_maintainers;
    }
    
    /**
     * Return the URL for the icon of the project, or false if one does not exist
     * @return string
     * @return boolean 
     */
    public function getIconURL() {
	if(strlen($this->project_icon_url) === 0) {
	    return false;
	} else {
	    return $this->project_icon_url;
	}
    }
    
    /**
     * Return an array of the websites associated with the project
     * @return array 
     */
    public function getWebsites() {
	$this->instanciate(); // Instanciate if these values do not exist
	return $this->project_websites;
    }
    
    /**
     * Return an array of documentation urls associated with the project
     * @return array 
     */
    public function getDoclinks() {
	$this->instanciate(); // Instanciate if these values do not exist
	return $this->project_doclinks;
    }
    
    /**
     * Return an array of blog urls associated with the project
     * @return array
     */
    public function getBlogs() {
	$this->instanciate(); // Instanciate if these values do not exist
	return $this->project_blogs;
    }
    
    /**
     * Return the Eclipse Update URL
     * @return String
     */
    public function getEclipse_u_url() {
	$this->instanciate(); // Instanciate if these values do not exist
	return $this->project_eclipse_u_url;
    }
    
    /**
     * Return URIs of the RSS Feeds
     * @return array 
     */
    public function getRSSfeeds() {
	$this->instanciate(); // Instanciate if these values do not exist
	return $this->project_rssfeeds;
    }
    
    /**
     * Return Required Depencies for the project
     * @return array 
     */
    public function getDepdReq() {
	$this->instanciate(); // Instanciate if these values do not exist
	return $this->project_depd_req;
    }
    
    /**
     * Return Optional Dependencies for the project
     * @return array
     */
    public function getDepdRec() {
	$this->instanciate(); // Instanciate if these values do not exist
	return $this->project_depd_rec;
    }
    
    /**
     * Does this project have a twitter account to display?
     * @return boolean 
     */
    public function hasTwitter() {
	$this->instanciate(); // Instanciate if these values do not exist
	if($this->project_displayTwitter !== false) {
	    return true;
	} else {
	    return false;
	}
    }
    
    /**
     * Return a list of twitter accounts associated with this project
     * @return array 
     */
    public function getTwitters() {
	$this->instanciate(); // Instanciate if these values do not exist
	return $this->project_twitters;
    }
    
    /**
     * Return the Twitter account which should be displayed, or false otherwise
     * @return string/boolean 
     */
    public function getTwitterToDisplay() {
	$this->instanciate(); // Instanciate if these values do not exist
	return $this->project_displayTwitter;
    }
    
    /**
     * Return an array of project tags which the project has associated with it
     * @return array 
     */
    public function getTags() {
	$this->instanciate();
	return $this->project_tags;
    }
    
    /**
     * Get the number of ratings the project has received
     * @return double
     */
    public function getNumberOfRatings() {
	$this->instanciate();
	return $this->project_num_rating;
    }
    
    /**
     * Get the average (SUM/NUMBER) rating of the project
     * @return type 
     */
    public function getAverageRating() {
	$this->instanciate();
	return $this->project_avg_rating;
    }
    
    public function getIsVisible() {
	return ($this->project_visible == '1' ? true : false);
    }
    
    /**
     * Return a boolean based on if the user (specified by ID) has rated this project
     * WARNING: DO NOT PASS UNSANITISED USER ID INFORMATION, SANITISE IT FIRST.
     * @global integer/string $sql_conx
     * @param int $userID
     * @return boolean 
     */
    public function hasUserRated($userID) {
	global $sql_conx;
	$query = $sql_conx->query("SELECT COUNT(`rating`) FROM `tool_rating` WHERE `uid`='{$userID}' AND `pid`='{$this->project_id}' LIMIT 1;");
	if($query !== false) {
	    if($query->num_rows === 1) {
		$row = $query->fetch_row();
		if($row[0] == '1') {
		    return true;
		}
	    }
	}
	
	return false;
    }
    
    /**
     * Create a rating on this project
     * TODO: Update Information in the class as well as the database
     * WARNING: DO NOT PASS UNSANITISED VALUES INTO THIS CLASS. SANITISE THEM FIRST.
     * @global Stash $UserStash
     * @global integer/string $sql_conx
     * @param int $userid
     * @param string $value
     * @return boolean 
     */
    public function addRating($userid, $value) {
	global $UserStash, $sql_conx;
	// On calling this function, ensure you have a valid userid!
	$value = floor($value);
	$value = (int)$value;
	if($value >= 0 && $value <= 5) {
	    try {
		$UserInstance =& $UserStash->get(((int)$userid));
		$query = $sql_conx->query("INSERT INTO `tool_rating` ( `pid` , `uid` , `rating` ) VALUES ( '{$this->project_id}', '".$UserInstance->get_userid()."' ,  '{$value}' )");
		return true;
	    } catch(InvalidUserException $e) {
		return false;
	    }
	}
    }
    
    /**
     * Can the user specified edit this project?
     * @param int $userid
     * @return boolean 
     */
    public function userCanEdit($userid, $overrideifadmin = true) {
	global $_LOGGEDINUSER;
	if(isset($_LOGGEDINUSER) === true && $_LOGGEDINUSER->get_priv_adm() === true && $overrideifadmin) {
	    return true;
	} else {
	    $this->instanciate();
	    if(in_array((int)$userid, $this->getAllMaintainers())) {
		return true;
	    } else {
		return false;
	    }
	}
    }
    
    /**
     * Can the user specified administrate this project?
     * @param int $userid
     * @return boolean 
     */
    public function userCanAdmin($userid, $overrideifadmin = true) {
	global $_LOGGEDINUSER;
	if(isset($_LOGGEDINUSER) === true && $_LOGGEDINUSER->get_priv_adm() === true && $overrideifadmin) {
	    return true;
	} else {
	    $this->instanciate();
	    if($userid == $this->project_lead_maintainer) {
		return true;
	    } else {
		return false;
	    }
	}
    }
    
    /**
     * Is the user a maintainer?
     * @param int $userid
     * @return boolean 
     */
    public function isMaintainer($userid) {
	$this->instanciate();
	if(in_array((int)$userid, $this->getAllMaintainers())) {
	    return true;
	} else {
	    return false;
	}
    }
    
    /**
     * Call to update the timestamp of when the project was edited.
     * @global integer/string $sql_conx 
     */
    public function updateTriggered() {
	global $sql_conx;
	$now = time();
	$sql_conx->query("UPDATE `tool_project` SET `lastedit_time`='{$now}' WHERE `pid`='{$this->project_id}';");
	// We wont error check this - It will have 500'd before we even get here
    }
    
    /**
     * Logger for updates.
     * @global integer/string $sql_conx
     * @param int $userid
     * @param ProjectEditType::x $type
     * @param array $info - See comment inside method
     */
    public function updateWrite($userid, $type, array $info) {
	/**
	 * 000: AddTag			    Array( $new_tag );
	 * 001: DelTag			    Array( $del_tag );
	 * 002: AddSite			    Array( $new_site );
	 * 003: DelSite			    Array( $del_site );
	 * 004: AddBlog			    Array( $add_blog );
	 * 005: DelBlog			    Array( $del_blog );
	 * 006: AddDocs			    Array( $add_docs );
	 * 007: DelDocs			    Array( $del_docs );
	 * 008: AddTwitter		    Array( $add_twitter );
	 * 009: DelTwitter		    Array( $del_twitter );
	 * 010: ChangeActiveTwitter:	    Array( $old_active, $new_active );
	 * 011: AddMaintainer:		    Array( $new_user ); 
	 */
	global $sql_conx;
	if(isset($info[0]) === true) {
	    $info1 = "'".$sql_conx->real_escape_string($info[0])."'";
	} else {
	    $info1 = 'NULL';
	}
	if(isset($info[1]) === true) {
	    $info2 = "'".$sql_conx->real_escape_string($info[1])."'";
	} else {
	    $info2 = 'NULL';
	}
	$pid = $this->getID();
	$now = time();
	$sql_conx->query("INSERT INTO `tool_logger` ( `pid` , `uid_editor` , `time` , `type`, `info1` , `info2` ) VALUES ( '{$pid}', '{$userid}' , '{$now}', '{$type}', {$info1} , {$info2} );");
    }
}

?>
